package com.iwindplus.gateway.server.domain.converter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.iwindplus.boot.domain.constant.CommonConstant.MetadataConstant;
import com.iwindplus.boot.domain.enums.BaseEnum;
import com.iwindplus.boot.domain.enums.EnableStatusEnum;
import com.iwindplus.mgt.domain.dto.system.ServerDTO;
import com.iwindplus.mgt.domain.dto.system.ServerRouteParamDTO;
import com.iwindplus.mgt.domain.vo.system.ServerRouteDefinitionVO;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;

/**
 * 服务路由对象转换器.
 *
 * @author zengdegui
 * @since 2018/9/1
 */
@Slf4j
public class RouteDefinitionConverter {

    /**
     * 转换
     *
     * @param source 源
     * @return RouteDefinition
     */
    public static RouteDefinition convertToRouteDefinition(ServerRouteDefinitionVO source) {
        String id = source.getId();
        String uri = source.getUri();
        List<ServerRouteParamDTO> predicateList = source.getPredicates();
        List<ServerRouteParamDTO> filterList = source.getFilters();
        final Map<String, Object> metadata = source.getMetadata();
        if (CharSequenceUtil.isBlank(id)) {
            return null;
        }
        if (CharSequenceUtil.isBlank(uri)) {
            return null;
        }
        if (CollUtil.isEmpty(predicateList)) {
            return null;
        }
        if (CollUtil.isEmpty(filterList)) {
            return null;
        }
        final boolean metadataFlag = MapUtil.isEmpty(metadata)
            || (MapUtil.isNotEmpty(metadata) && Objects.isNull(metadata.get(MetadataConstant.NAME)));
        if (Boolean.TRUE.equals(metadataFlag)) {
            return null;
        }

        RouteDefinition result = new RouteDefinition();
        result.setId(id);
        result.setUri(URI.create(uri));
        result.setPredicates(RouteDefinitionConverter.buildPredicateDefinition(predicateList));
        result.setFilters(RouteDefinitionConverter.buildFilterDefinition(filterList));
        result.setOrder(source.getOrder());
        result.setMetadata(source.getMetadata());
        return result;
    }


    /**
     * 转换
     *
     * @param source 源
     * @return ServerDTO
     */
    public static ServerDTO convertToServer(ServerRouteDefinitionVO source) {
        String routeId = source.getId();
        String uri = source.getUri();
        List<ServerRouteParamDTO> predicateList = source.getPredicates();
        List<ServerRouteParamDTO> filterList = source.getFilters();
        Map<String, Object> metadata = source.getMetadata();
        final boolean metadataFlag = MapUtil.isEmpty(metadata)
            || (MapUtil.isNotEmpty(metadata) && Objects.isNull(metadata.get(MetadataConstant.NAME)));
        if (CharSequenceUtil.isBlank(routeId)
            || CharSequenceUtil.isBlank(uri)
            || CollUtil.isEmpty(predicateList)
            || CollUtil.isEmpty(filterList)
            || Boolean.TRUE.equals(metadataFlag)) {
            return null;
        }

        final Boolean enabled = source.getEnabled();
        Integer status = Objects.isNull(enabled) || Boolean.TRUE.equals(enabled)
            ? EnableStatusEnum.ENABLE.getValue() : EnableStatusEnum.DISABLE.getValue();
        ServerDTO result = ServerDTO.builder()
            .name(metadata.get(MetadataConstant.NAME).toString())
            .routeId(routeId)
            .uri(uri)
            .predicates(predicateList)
            .filters(filterList)
            .status(BaseEnum.fromValue(status, EnableStatusEnum.class))
            .metadata(metadata)
            .seq(source.getOrder())
            .build();
        return result;
    }

    static List<PredicateDefinition> buildPredicateDefinition(List<ServerRouteParamDTO> predicateList) {
        return predicateList.stream().map(m -> {
            PredicateDefinition param = new PredicateDefinition();
            param.setName(m.getName());
            param.setArgs(m.getArgs());
            return param;
        }).collect(Collectors.toCollection(ArrayList::new));
    }

    static List<FilterDefinition> buildFilterDefinition(List<ServerRouteParamDTO> filterList) {
        return filterList.stream().map(m -> {
            FilterDefinition param = new FilterDefinition();
            param.setName(m.getName());
            param.setArgs(m.getArgs());
            return param;
        }).collect(Collectors.toCollection(ArrayList::new));
    }
}
